package com.qipay.admin.withdraw.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.qipay.admin.withdraw.vo.*;
import com.qipay.baas.model.*;
import com.qipay.card.service.CardBindService;
import com.qipay.configuration.config.ConfigurationEnum;
import com.qipay.configuration.service.ConfigurationService;
import com.qipay.cp.CpService;
import com.qipay.cp.config.CpAccountStateEnum;
import com.qipay.cpaccount.CpAccountService;
import com.qipay.cpinfo.service.CpInfoService;
import com.qipay.holiday.service.HolidayService;
import com.qipay.order.service.OrderService;
import com.qipay.security.UserDetailsServiceImpl;
import com.qipay.sms.service.SmsService;
import com.qipay.user.entity.UserLevel;
import com.qipay.utils.NumGeneratorUtils;
import com.qipay.withdraw.config.WithdrawOperationEnum;
import com.qipay.withdraw.config.WithdrawStateAliasEnum;
import com.qipay.withdraw.config.WithdrawStateEnum;
import com.qipay.withdraw.config.WithdrawTypeEnum;
import com.qipay.withdraw.service.WithdrawService;
import io.jsonwebtoken.lang.Assert;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.servlet.ModelAndView;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;

/**
 * Creator: Yao
 * Date:    2017/9/27
 * For:
 * Other:
 */
@SuppressWarnings("ConstantConditions")
@Service
public class WithdrawLogicService {

    Logger logger = LogManager.getLogger(WithdrawLogicService.class);

    @Autowired
    WithdrawService withdrawService;
    @Autowired
    CpAccountService cpAccountService;
    @Autowired
	OrderService orderService;
    @Autowired
    CpInfoService cpInfoService;
    @Autowired
    ConfigurationService configurationService;
    @Autowired
    CpService cpService;
    @Autowired
    NumGeneratorUtils numGeneratorUtils;
    @Autowired
    CardBindService cardBindService;
    @Autowired
    SmsService smsService;



    private final String configWithdrawMin = "10000";
    private final String configWithdrawMaxHoliday = "5000000";
    private final String configWithdrawMaxWork = "50000000";
    private final String withdrawAdvanceLimit = "100";


    public long countApply(){
        return withdrawService.countApply();
    }

    public WithdrawVo get(Long withdrawId) {

    // check
        Assert.notNull(withdrawId);

    // init
    WithdrawVo q = null;
    CpWithdrawTradlog p = withdrawService.get(withdrawId);
        if (null != p) {
        q = new WithdrawVo();
        BeanUtils.copyProperties(p, q);
        q.setAmount(Float.valueOf(p.getAmount()));
        q.setActualAmount(Float.valueOf(p.getActualAmount()));
        fill(q);
    }

        return q;
}

    @Transactional
    public synchronized void save(UserDetailsServiceImpl.LoginUserDetails loginUser, WithdrawVo vo) {
        //校验基本信息
        Assert.notNull(loginUser);
        Assert.notNull(vo);
        Cp cp = cpService.getOneById(Long.parseLong(loginUser.getUsername()));
        Assert.notNull(cp, "未找到指定商户");
        Assert.isTrue(null != cp.getAccountState() && cp.getAccountState().equals(CpAccountStateEnum.ENABLED.getState()), "该 CP 账号未启用");
		String pwd = vo.getWithdrawPassword();
		Assert.isTrue(StringUtils.isNotBlank(pwd) && cp.getWithdrawPassword().equalsIgnoreCase(pwd), "提现密码有误");

		//校验金额（订单表商户实际总金额-提现的）
		Long total = orderService.sumOrderByCp(cp.getId());
		Long withdrawTotal = withdrawService.count(cp.getId());
		if(total-withdrawTotal>(int)(vo.getAmount()*100)){
			vo.setState(WithdrawStateEnum.APPLY.getState());
		}else{
			vo.setState(WithdrawStateEnum.UN_PASS.getState());
			vo.setRemark("余额不足");
		}
        // init
        CpWithdrawTradlog withdraw = init(loginUser, vo);
        // save
        if (null == withdraw.getId() || 0 == withdraw.getId()) {
            // save
            withdrawService.save(withdraw);
            //发通知
//            smsService.withdrawInform(withdraw.getTradeNo());
            //清除code
            //codeCacheUtils.invalidateCode(cp.getBindMobile(),AliyunSMSTemplateEnum.MONYUNVALID.getCode());
        } else {
            withdrawService.update(withdraw);
        }
    }

    @Transactional
    public void cancel(UserDetailsServiceImpl.LoginUserDetails loginUser, Long withdrawId) {

        // check
        Assert.notNull(loginUser, "未提供用户信息");
        UserLevel level = loginUser.getLevel();
        Assert.isTrue(null != level && level.equals(UserLevel.ADMIN), "无权限");
        Assert.notNull(withdrawId, "未提供提现申请信息");
        CpWithdrawTradlog log = withdrawService.get(withdrawId);
        Assert.notNull(log, "未找到指定提现申请");
        Byte state = log.getState();
        Assert.isTrue(null != state && state.equals(WithdrawStateEnum.APPLY.getState()), "该提现申请已处理");

        // update
        log.setState(WithdrawStateEnum.CANCEL.getState());
        log.setFinishTime(new Date());
        log.setRemark("该申请已被取消");
        withdrawService.update(log);
        //withdraw(log, false);
    }

    @Transactional
    public void handPass(UserDetailsServiceImpl.LoginUserDetails loginUser, Long withdrawId, String remark) {
        // check
        Assert.notNull(loginUser, "未提供用户信息");
        UserLevel level = loginUser.getLevel();
        Assert.isTrue(null != level && level.equals(UserLevel.ADMIN), "无权限");
        Assert.notNull(withdrawId, "未提供提现申请信息");
        CpWithdrawTradlog log = withdrawService.get(withdrawId);
        Assert.notNull(log, "未找到指定提现申请");
        Assert.isTrue(StringUtils.isNotBlank(remark), "未指定手动出款的原因");

        // update
        log.setState(WithdrawStateEnum.PAYED.getState());
        log.setRemark(remark);
        log.setFinishTime(new Date());
        withdrawService.update(log);
    }

    @Transactional
    public void pass(UserDetailsServiceImpl.LoginUserDetails loginUser, Long withdrawId) {
        Assert.notNull(loginUser, "未提供用户信息");
        UserLevel level = loginUser.getLevel();
        Assert.isTrue(null != level && level.equals(UserLevel.ADMIN), "无权限");
        Assert.notNull(withdrawId, "未提供提现申请信息");
        CpWithdrawTradlog log = withdrawService.get(withdrawId);
        Assert.notNull(log, "未找到指定提现申请");

        log.setState(WithdrawStateEnum.PAYED.getState());
        log.setRemark("线上代付"+new BigDecimal(String.valueOf(log.getActualAmount())).divide(new BigDecimal(String.valueOf("100"))));
        log.setFinishTime(new Date());
        withdrawService.update(log);
    }

    @Transactional
    public void unPass(UserDetailsServiceImpl.LoginUserDetails loginUser, Long withdrawId, String remark) {

        // check
        Assert.notNull(loginUser, "未提供用户信息");
        UserLevel level = loginUser.getLevel();
        Assert.isTrue(null != level && level.equals(UserLevel.ADMIN), "无权限");
        Assert.notNull(withdrawId, "未提供提现申请信息");
        CpWithdrawTradlog log = withdrawService.get(withdrawId);
        Assert.notNull(log, "未找到指定提现申请");
        Assert.isTrue(StringUtils.isNotBlank(remark), "未指定不通过的原因");

        // update
        log.setState(WithdrawStateEnum.UN_PASS.getState());
        log.setRemark(remark);
        log.setFinishTime(new Date());
        withdrawService.update(log);
        //withdraw(log, false);
    }



    private void withdraw(CpWithdrawTradlog withdraw, boolean success) {

        // check
        Assert.notNull(withdraw);
        Long cpId = withdraw.getCpId();
        Integer amount = withdraw.getAmount();
        Byte type = withdraw.getType();
        Assert.notNull(cpId);
        Assert.notNull(amount);
        Assert.notNull(type);
        int base = success ? -1 : 1;

        // update
        cpAccountService.adjust(
                withdraw.getCpId(),
                base * Long.valueOf(amount),
                type.equals(WithdrawTypeEnum.AVAILABLE.getType()) ? base * amount : 0l,
                type.equals(WithdrawTypeEnum.ADVANCE.getType()) ? base * amount : 0l,
                0l,
                type.equals(WithdrawTypeEnum.ADVANCE.getType()) ? -base * amount : 0l);
    }

    private CpWithdrawTradlog init(UserDetailsServiceImpl.LoginUserDetails loginUser, WithdrawVo p) {

        // check
        Assert.notNull(loginUser);
        Assert.notNull(p);

        // init
        Long cpId = Long.valueOf(loginUser.getUsername());
        CpWithdrawTradlog q = new CpWithdrawTradlog();
        BeanUtils.copyProperties(p, q);
        q.setCpId(Long.valueOf(loginUser.getUsername()));
        q.setState(q.getState());
        q.setCreateTime(new Date());
        q.setAmount((int) (p.getAmount() * 100));
        q.setFee(q.getFee().multiply(new BigDecimal(100)));
        q.setActualAmount((int) (p.getActualAmount() * 100));
        q.setTradeNo(numGeneratorUtils.generate());
		q.setRemark(p.getRemark());
        // logic.init
        logicInit(q, cpId);

        return q;
    }


    @Autowired
    HolidayService holidayService;


    private void logicInit(CpWithdrawTradlog q, Long cpId) {
        // check
        Assert.notNull(q);
        Assert.notNull(cpId);
        // init.withdraw
        Byte operation = q.getOperation();
        Byte type = q.getType();
        Integer amount = q.getAmount();
        Assert.isTrue(null != operation && null != type && null != amount);
        BigDecimal fee = new BigDecimal(0);
        Float left = 0f;
        String info = WithdrawOperationEnum.getInfo(operation);

        // init.config
        Map<Byte, String> configMap = configurationService.map();
        Assert.isTrue(MapUtils.isNotEmpty(configMap));
        Long min = Long.valueOf(configMap.containsKey(ConfigurationEnum.WITHDRAW_MIN.getType()) ? configMap.get(ConfigurationEnum.WITHDRAW_MIN.getType()) : configWithdrawMin);
        Long max =
                holidayService.check(new Date())
                        ? (Long.valueOf(configMap.containsKey(ConfigurationEnum.WITHDRAW_MAX_HOLIDAY.getType()) ? configMap.get(ConfigurationEnum.WITHDRAW_MAX_HOLIDAY.getType()) : configWithdrawMaxHoliday))
                        : (Long.valueOf(configMap.containsKey(ConfigurationEnum.WITHDRAW_MAX_WORK.getType()) ? configMap.get(ConfigurationEnum.WITHDRAW_MAX_WORK.getType()) : configWithdrawMaxWork));
        Cp cp = cpService.getOneById(cpId);
        Float cost = null != cp.getCostWithdraw() ? cp.getCostWithdraw() : 0f;

        fee = new BigDecimal(cost);
        Assert.isTrue(amount >= min, String.format("%s金额低于系统规定最小额度", info));
        Assert.isTrue(amount <= max, String.format("%s金额超出系统规定最大额度", info));
        // fill
        q.setFee(fee);
        left = Float.valueOf(amount - fee.floatValue());
        q.setActualAmount(left.intValue());
    }

    public void initInfo(ModelAndView view, UserDetailsServiceImpl.LoginUserDetails loginUser) {
        // card.list
        view.addObject("cardList", fillBindCards(loginUser.getId()));
        Long cpId = Long.valueOf(loginUser.getUsername());

        Long orderTotal = orderService.sumOrderByCp(cpId);
        Long widthdrawTotal = withdrawService.count(cpId);
        // account
        CpAccount account = new CpAccount();
        account.setCpId(cpId);
        account.setBalance(orderTotal-widthdrawTotal);
        account.setBalanceAvailable(orderTotal-widthdrawTotal);
        account.setBalanceAdvance(0L);
        account.setBalanceDisabled(0L);
        view.addObject("account", account);
        // config
        view.addObject("config", filter(configurationService.map(), loginUser));
    }

    private Map<Byte, String> filter(Map<Byte, String> map, UserDetailsServiceImpl.LoginUserDetails loginUser) {
        // check
        Assert.notNull(loginUser);
        // config
        if (MapUtils.isNotEmpty(map)) {
            for (Byte key : map.keySet()) {
                System.out.println("key=="+key);
                switch (ConfigurationEnum.convert(key)) {
                    case WITHDRAW_MIN:
                    case WITHDRAW_MAX_HOLIDAY:
                    case WITHDRAW_MAX_WORK:
                    case WITHDRAW_AVAILABLE_COST:
                        map.put(
                                key,
                                (Double.valueOf(map.get(key)) / 100.0) + "");
                        break;
                }
            }
        } else {
            map = new HashMap<>();
        }

        // cp
        if (loginUser.getLevel() != UserLevel.ADMIN) {
            Cp cp = cpService.getOneById(Long.valueOf(loginUser.getUsername()));
            if (null != cp) {
                map.put(ConfigurationEnum.WITHDRAW_ADVANCE_RATE.getType(), cp.getFeeWithdrawAdvince().toString());
                map.put(ConfigurationEnum.WITHDRAW_AVAILABLE_COST.getType(), cp.getCostWithdraw() / 100.0 + "");
            }
        }

        return map;
    }

    private List<BindVo> fillBindCards(Integer userId) {

        // check
        Assert.notNull(userId);

        // init
        List<CardBind> cardList = cardBindService.listByUserId(Long.valueOf(userId));
        List<BindVo> list = new ArrayList<>();

        // fill
        list.add(new BindVo("", "", "", "测试用户", "15888888888"));
            if (CollectionUtils.isNotEmpty(cardList)) {
            for (CardBind p : cardList) {
                list.add(new BindVo(p.getBank(), p.getSubBank(), p.getCard(), p.getName(), p.getMobile()));
            }
        }

        return list;
    }

    /**
     * 解析province.json为集合返回到页面
     * @return
     */
    public  List<Province> getProvince() {

        String path="C:\\Users\\Administrator\\Desktop\\AreaAndBanks\\data\\provinces.json";
        List<Province> provinces = null;
        try {
            String file=FileUtils.readFileToString(new File(path),"utf-8");
            String data= String.valueOf(JSON.parse(file));
            provinces = JSONArray.parseArray(data,Province.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return provinces;
    }

    /**
     * 通过bankName、cityId两个参数从数据库中查询支行，返回集合给页面
     * @return
     */
    public List<Banks> getBank(String province,String city,String bank) {
        List<Banks> banks = withdrawService.findSubbranch(province,city,bank);
        return banks;
    }

    /**
     * 解析cities.json为集合返回到页面
     * @param provinceCode
     * @return
     */
    public  List<Cities> getCities(Integer provinceCode) {
        String path="C:\\Users\\Administrator\\Desktop\\AreaAndBanks\\data\\cities.json";
        List<Cities>  cities1=new ArrayList<>();
        try {
            String file=FileUtils.readFileToString(new File(path),"utf-8");
            String data= String.valueOf(JSON.parse(file));
            List<Cities> cities0=JSONArray.parseArray(data,Cities.class);
            for (Cities c : cities0) {
               if(provinceCode.equals(Integer.parseInt(c.getParent_code()))){
                   cities1.add(c);
               }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return cities1;
    }



    public long count(Long cpId, Byte state,String from, String to) {
        return withdrawService.count(cpId,state, from, to);
    }

    //统计行信息
    public CountWithdrawInfoVo countInfo(Long cpId,Byte state, String from, String to) {
        CountWithdrawInfoVo countWithdrawInfoVo = new CountWithdrawInfoVo();
        CpWithdrawTradlog cpWithdrawTradlog = withdrawService.countInfo(cpId,state, from, to);
        if (cpWithdrawTradlog == null) {
            countWithdrawInfoVo.setAmountCount(0);
            countWithdrawInfoVo.setFeeCount(BigDecimal.ZERO);
            countWithdrawInfoVo.setActualAmountCount(0);
        } else {
            countWithdrawInfoVo.setAmountCount(cpWithdrawTradlog.getAmount());
            countWithdrawInfoVo.setFeeCount(cpWithdrawTradlog.getFee());
            countWithdrawInfoVo.setActualAmountCount(cpWithdrawTradlog.getActualAmount());
        }
        return countWithdrawInfoVo;
    }

    public List<WithdrawVo> list(Long cpId,Byte state, String from, String to, Integer page, Integer pageSize) {

        // check
        Assert.notNull(page);
        Assert.notNull(pageSize);

        return fill(withdrawService.list(cpId,state, from, to, page, pageSize));
    }
    public List<WithdrawExportVo> exportList(Long cpId, Byte state, String from, String to, Integer page, Integer pageSize) {

        // check
        Assert.notNull(page);
        Assert.notNull(pageSize);

        return exportfill(withdrawService.list(cpId,state, from, to, page, pageSize));
    }

    public List<CpWithdrawTradlog> listByCpId(Long cpId,Byte state) {
        return withdrawService.listByCpId(cpId,state);
    }


    private List<WithdrawExportVo> exportfill(List<CpWithdrawTradlog> list) {
        // init
        List<WithdrawExportVo> voList = new ArrayList<>();

        // fill
        if (CollectionUtils.isNotEmpty(list)) {
            for (CpWithdrawTradlog p : list) {
                WithdrawExportVo q = new WithdrawExportVo();
                BeanUtils.copyProperties(p, q);
                q.setAmount(Float.valueOf(p.getAmount()));
                q.setActualAmount(Float.valueOf(p.getActualAmount()));
                exportfill(q,p.getType(),p.getOperation(),p.getState());
                voList.add(q);
            }
        }

        return voList;
    }
    private void exportfill(WithdrawExportVo withdraw,Byte type,Byte operation,Byte state) {
        if (null == withdraw)
            return;
        CpInfo info = cpInfoService.get(withdraw.getCpId());
        withdraw.setCpName(null == info ? "" : info.getName());
        withdraw.setTypeName(WithdrawTypeEnum.getInfo(type));
        withdraw.setOperationName(WithdrawOperationEnum.getInfo(operation));
        withdraw.setStateName(WithdrawStateAliasEnum.getInfo(state));
        withdraw.setAmount(withdraw.getAmount() / 100.0f);
        withdraw.setFee(withdraw.getFee().divide(new BigDecimal(100.0f)));
        withdraw.setActualAmount(withdraw.getActualAmount() / 100.0f);
        withdraw.setRemark(StringUtils.isBlank(withdraw.getRemark()) ? "" : withdraw.getRemark());
    }

    private List<WithdrawVo> fill(List<CpWithdrawTradlog> list) {

        // init
        List<WithdrawVo> voList = new ArrayList<>();

        // fill
        if (CollectionUtils.isNotEmpty(list)) {
            for (CpWithdrawTradlog p : list) {
                WithdrawVo q = new WithdrawVo();
                BeanUtils.copyProperties(p, q);
                q.setAmount(Float.valueOf(p.getAmount()));
                q.setActualAmount(Float.valueOf(p.getActualAmount()));
                fill(q);
                voList.add(q);
            }
        }

        return voList;
    }

    private void fill(WithdrawVo withdraw) {

        if (null == withdraw)
            return;

        CpInfo info = cpInfoService.get(withdraw.getCpId());
        withdraw.setCpName(null == info ? "" : info.getName());
        withdraw.setTypeName(WithdrawTypeEnum.getInfo(withdraw.getType()));
        withdraw.setOperationName(WithdrawOperationEnum.getInfo(withdraw.getOperation()));
        withdraw.setStateName(WithdrawStateAliasEnum.getInfo(withdraw.getState()));
        withdraw.setAmount(withdraw.getAmount() / 100.0f);
        withdraw.setFee(withdraw.getFee().divide(new BigDecimal(100.0f)));
        withdraw.setActualAmount(withdraw.getActualAmount() / 100.0f);
        withdraw.setRemark(StringUtils.isBlank(withdraw.getRemark()) ? "" : withdraw.getRemark());
    }

    public void initConfig(ModelAndView view, UserDetailsServiceImpl.LoginUserDetails loginUser) {

        // check
        Assert.notNull(view);

        view.addObject("config", filter(configurationService.map(), loginUser));
    }

    public void fillApplyArgs(ModelAndView view, UserDetailsServiceImpl.LoginUserDetails loginUser) {

        // check
        Assert.notNull(view);
        Assert.notNull(loginUser);
        if (!"admin".equals(loginUser.getUsername())){
            Cp cp = cpService.getOneById(Long.valueOf(loginUser.getUsername()));
            Assert.notNull(cp, "未找到指定 CP");
            // fill
            view.addObject("password", cp.getWithdrawPassword());
        }
        // fill
        view.addObject("level", loginUser.getLevel());
    }


    public String valid(UserDetailsServiceImpl.LoginUserDetails loginUser) {
        // check
        Assert.notNull(loginUser);
        Cp cp = cpService.getOneById(Long.parseLong(loginUser.getUsername().equals("admin")?String.valueOf(loginUser.getPid()):loginUser.getUsername()));
        Assert.notNull(cp, "未找到指定 CP");
        String phone = cp.getBindMobile();
        Assert.isTrue(StringUtils.isNotBlank(phone), "CP 未绑定手机");

        // sms
        return smsService.valid(phone);
    }

//

}
